package br.com.sovi.gwt.client.controleunitario;

import java.util.ArrayList;
import java.util.List;

import br.com.sovi.gwt.client.controleunitario.infowindow.ControleUnitarioInfoWindow;
import br.com.sovi.gwt.client.controleunitario.infowindow.ControleUnitarioInfoWindowListener;
import br.com.sovi.gwt.client.entity.ControleUnitario;
import br.com.sovi.gwt.client.entity.ControleUnitarioPoint;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.maps.client.MapType;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.LargeMapControl3D;
import com.google.gwt.maps.client.event.MarkerClickHandler;
import com.google.gwt.maps.client.geocode.DirectionQueryOptions;
import com.google.gwt.maps.client.geocode.DirectionResults;
import com.google.gwt.maps.client.geocode.Directions;
import com.google.gwt.maps.client.geocode.DirectionsCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.overlay.PolyStyleOptions;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Timer;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.layout.FitLayout;

public class ControleUnitarioCentralPanel extends Panel implements ControleUnitarioListener,
		ControleUnitarioInfoWindowListener {

	private MapWidget map;

	private List<ControleUnitarioMarker> markers;

	private ControleUnitarioInfoWindow controleUnitarioInfoWindow;

	public ControleUnitarioCentralPanel() {
		this.setLayout(new FitLayout());

		map = new MapWidget();
		map.setSize("100%", "100%");
		map.addControl(new LargeMapControl3D());

		controleUnitarioInfoWindow = new ControleUnitarioInfoWindow(map, this);

		new Timer() {
			@Override
			public void run() {
				map.checkResizeAndCenter();
			}
		}.schedule(1);

		this.add(map);
		this.doLayout();
	}

	public void populateScreen(final ControleUnitario controleUnitario) {
		map.clearOverlays();

		List<ControleUnitarioPoint> pontos = controleUnitario.getPontos();

		if (pontos == null || pontos.size() <= 0) {
			GWT.log("Nenhum ponto encontrado para ser mostrado.", null);
			return;
		}

		markers = new ArrayList<ControleUnitarioMarker>();

		for (int indexPoint = 0; indexPoint < pontos.size(); indexPoint++) {
			ControleUnitarioPoint ponto = pontos.get(indexPoint);
			LatLng latLng = LatLng.newInstance(ponto.getPoint().getLatitude(), ponto.getPoint().getLongitude());

			ControleUnitarioMarker marker = new ControleUnitarioMarker(ponto);

			markers.add(marker);

			marker.addMarkerClickHandler(new MarkerClickHandler() {
				@Override
				public void onClick(MarkerClickEvent event) {
					controleUnitarioInfoWindow.showInfoWindow((ControleUnitarioMarker) event.getSender());
				}
			});

			map.addOverlay(marker);

			// new Geocoder().getLocations(latLng, new LocationCallback() {
			// @Override
			// public void onSuccess(JsArray<Placemark> locations) {
			// GWT.log("Recebendo as locations...", null);
			// locationsMap = new HashMap<ControleUnitarioPoint,
			// JsArray<Placemark>>();
			// locationsMap.put(ponto, locations);
			// dots.add(ponto);
			// }
			//
			// @Override
			// public void onFailure(int statusCode) {
			// GWT.log("Falha no geocoding: " + statusCode, null);
			// dots.add(ponto);
			// }
			// });

		}

		if (!markers.isEmpty()) {
			map.setCenter(markers.get(0).getLatLng(), 17);
			controleUnitarioInfoWindow.showInfoWindow(markers.get(0));
		}

		if (controleUnitario.isTrafego()) {
			map.setCurrentMapType(MapType.getNormalMap());
			createPolylines();
		}
		else {
			map.setCurrentMapType(MapType.getSatelliteMap());

			if (controleUnitario.isRastro()) {
				createRastro(controleUnitario);
			}
		}

		if (controleUnitario.getZoom() > 0) {
			map.setZoomLevel(controleUnitario.getZoom());
		}

		this.doLayout();
	}

	/**
	 * Para criar linhas simples sem seguir as ruas.
	 * @param controleUnitario
	 */
	private void createRastro(ControleUnitario controleUnitario) {
		List<ControleUnitarioPoint> points = controleUnitario.getPontos();

		LatLng[] lls = new LatLng[points.size()];

		for (int i = 0; i < points.size(); i++) {
			ControleUnitarioPoint controleUnitarioPoint = points.get(i);
			lls[i] = LatLng.newInstance(controleUnitarioPoint.getPoint().getLatitude(), controleUnitarioPoint
					.getPoint().getLongitude());
		}

		Polyline p = new Polyline(lls);

		PolyStyleOptions instance = PolyStyleOptions.getInstance();
		instance.setColor("red");
		instance.setOpacity(30);

		p.setStrokeStyle(instance);
		map.addOverlay(p);
	}

	/**
	 * Para criar o efeito de tráfego, com linha nas ruas
	 */
	private void createPolylines() {
		new Timer() {
			@Override
			public void run() {
				GWT.log("Iniciando recurso de polyline: " + markers.size(), null);

				DirectionQueryOptions directionQueryOptions = new DirectionQueryOptions();
				directionQueryOptions.setRetrievePolyline(true);

				for (int i = 0; i < markers.size(); i++) {
					ControleUnitarioMarker currentEntry = markers.get(i);
					ControleUnitarioMarker nextEntry = null;

					try {
						nextEntry = markers.get((i + 1));
					}
					catch (Exception e) {
						continue; // Se não existe próximo elemento, é só
						// finalizar o loop
					}

					{
						JsArray<Placemark> currentArray = currentEntry.getJsArray();
						JsArray<Placemark> nextArray = nextEntry.getJsArray();

						if (currentArray == null && nextArray == null) {
							GWT.log("Alguma Location não existe!", null);
							continue;
						}
					}

					// criar as directions na tela.
					Directions.load(createDirectionQuery(currentEntry, nextEntry), directionQueryOptions,
							new DirectionsCallback() {
								@Override
								public void onSuccess(DirectionResults result) {
									GWT.log("Recebendo resultados.", null);
									map.addOverlay(result.getPolyline());
								}

								@Override
								public void onFailure(int statusCode) {
									GWT.log("Erro ao capturar directions: " + statusCode, null);
								}
							});
				}
			}

			private String createDirectionQuery(ControleUnitarioMarker currentEntry, ControleUnitarioMarker nextEntry) {
				StringBuilder sb = new StringBuilder("from: ");

				JsArray<Placemark> currentArray = currentEntry.getJsArray();
				JsArray<Placemark> nextArray = nextEntry.getJsArray();

				sb.append(currentArray.get(0).getAddress());
				sb.append(" to:");
				sb.append(nextArray.get(0).getAddress());

				return sb.toString();
			}

		}.schedule(2500);
	}

	@Override
	public void onUpdate(ControleUnitario controleUnitario) {
		populateScreen(controleUnitario);
	}

	@Override
	public ControleUnitarioMarker getNextMarker(ControleUnitarioMarker marker) {
		int indexOf = markers.indexOf(marker);
		int ultimoPonto = markers.size() - 1;

		if (indexOf >= 0 && indexOf < ultimoPonto) {
			return markers.get(indexOf + 1);
		}

		return null;
	}

	@Override
	public ControleUnitarioMarker getPreviousMarker(ControleUnitarioMarker marker) {
		int indexOf = markers.indexOf(marker);
		int ultimoPonto = markers.size() - 1;

		if (indexOf > 0 && indexOf <= ultimoPonto) {
			return markers.get(indexOf - 1);
		}

		return null;
	}

}
